package org.jsola.hr.service.async.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsola.admin.constant.AsyncTaskTypeEnum;
import org.jsola.admin.constant.TaskStatusEnum;
import org.jsola.admin.constant.TaskSubjectResultEnum;
import org.jsola.admin.dto.AsyncTaskDetailAddDTO;
import org.jsola.admin.entity.AsyncTaskDO;
import org.jsola.admin.entity.DictItemDO;
import org.jsola.admin.entity.SiteDO;
import org.jsola.admin.vo.AreaReduceVO;
import org.jsola.common.DateKit8;
import org.jsola.common.StrKit;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.CacheKeyKit;
import org.jsola.hr.common.DateUtils;
import org.jsola.hr.common.RegUtils;
import org.jsola.hr.constant.*;
import org.jsola.hr.dto.CheckInPeopleUpdateDTO;
import org.jsola.hr.dto.excel.EmpInfoImportDTO;
import org.jsola.hr.entity.CheckInPeopleDO;
import org.jsola.hr.entity.EmpInfoDO;
import org.jsola.hr.example.PersonalEventExample;
import org.jsola.hr.provider.IAdminProviderService;
import org.jsola.hr.provider.INoticeProviderService;
import org.jsola.hr.provider.IUserProviderService;
import org.jsola.hr.service.*;
import org.jsola.hr.service.async.IAsyncEmpInfoDetailService;
import org.jsola.hr.service.async.IAsyncEmpInfoImportService;
import org.jsola.hr.service.notice.ISendNoticeService;
import org.jsola.hr.task.SendNoticeTask;
import org.jsola.hr.vo.ChannelVO;
import org.jsola.hr.vo.CompanyVO;
import org.jsola.hr.vo.EmpImportExcelVO;
import org.jsola.hr.vo.EmpInfoVO;
import org.jsola.notice.constant.NoticeConstants;
import org.jsola.notice.constant.ReceiveDateTypeEnum;
import org.jsola.notice.vo.NoticeRuleSettingVO;
import org.jsola.user.common.PinyinKit;
import org.jsola.user.core.TokenUser;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.EmpInfoStatus.HAS_ENTRY;
import static org.jsola.hr.constant.EmpSocStatus.NOT_SOC;
import static org.jsola.hr.constant.FormalTypeEnum.FORMAL;
import static org.jsola.hr.constant.FormalTypeEnum.PROBATION;

/**
 * 花名册人员异步导入
 *
 * @author wcl
 */
@Slf4j
@Service("hrAsyncEmpInfoImportServiceImpl")
public class AsyncEmpInfoImportServiceImpl implements IAsyncEmpInfoImportService {

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private IAsyncEmpInfoDetailService asyncEmpInfoDetailService;

    @Autowired
    private IAdminProviderService adminProviderService;

    @Autowired
    private IChannelService channelService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private SendNoticeTask sendNoticeTask;

    @Autowired
    private ISendNoticeService sendNoticeService;

    @Autowired
    private ICheckInPeopleService checkInPeopleService;

    @Autowired
    private INoticeProviderService noticeProviderService;

    @Autowired
    private IUserProviderService userProviderService;

    @Autowired
    private IPersonalEventService personalEventService;

    @Autowired
    private IEmpSocRelatedService empSocRelatedService;

    @Autowired
    private ICompanyService companyService;

    /**
     * 国籍(地区), 默认中国
     */
    private static final String NATIONALITY = "中国";

    /**
     * 任职受雇从业类型(默认1:雇员)
     */
    private static final String EMPLOYMENT_TYPE = "1";

    /**
     * 执行结果：成功
     */
    private static final String SUBJECT_RESULT_SUCCESS = "1";

    /**
     * 执行结果：失败
     */
    private static final String SUBJECT_RESULT_FAIL = "0";

    @Async
    @Override
    public void asyncBatchSave(EmpImportExcelVO empImportExcelVO, String companyId, boolean isAdd, TokenUser tokenUser) {
        //  创建员工信息
        List<EmpInfoDO> empInfoDOList;
        if (isAdd) {
            empInfoDOList = empImportExcelVO.getEmpInfoAddList();
        } else {
            empInfoDOList = empImportExcelVO.getEmpInfoUpdateList();
        }
        //  创建异步任务记录
        CompanyVO companyVO = companyService.selectBaseCompanyById(companyId, tokenUser.getSiteId());
        AsyncTaskDO asyncTaskDO = new AsyncTaskDO();
        asyncTaskDO.setTaskName("用工单位：【" + companyVO.getName() + "】-花名册批量导入-" + DateKit8.format(new Date(), "yyyy-MM-dd HH:mm"));
        asyncTaskDO.setTaskStart(new Date());
        asyncTaskDO.setTotalTask(empInfoDOList.size());
        asyncTaskDO.setTaskStatus(TaskStatusEnum.JIN_XING_ZHONG.getValue());
        asyncTaskDO.setTaskType(AsyncTaskTypeEnum.EMP_IMPORT.getValue());
        asyncTaskDO.setTaskMethod("asyncBatchSave");
        AsyncTaskDO asyncTask = adminProviderService.saveAsyncTask(asyncTaskDO, tokenUser);
        String asyncTaskId = asyncTask.getId();
        log.info("====开始多线程导入,任务id：{}====", asyncTaskId);
        //  1.校验redis 是否存在正在导入操作
        String lockKey = CacheKeyKit.getEmpInfoImportKey(companyId);
        RLock lock = redissonClient.getLock(lockKey);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后60分钟自动解锁
            hasLock = lock.tryLock(0, 60, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            log.error("异步导入员工信息失败：{}，lockKey：{}", e.getMessage(), lockKey);
            // 任务为失败
            adminProviderService.updateTaskFail(asyncTaskId);
            return;
        }
        if (!hasLock) {
            log.warn("花名册导入重复点击");
            // 任务为失败
            adminProviderService.updateTaskFail(asyncTaskId);
            return;
        }
        // 所有地区
        List<AreaReduceVO> areaList = adminProviderService.listAllArea();
        // 创建empInfo信息
        saveEmpInfoData(empInfoDOList, isAdd, companyId, areaList, tokenUser);
        // 创建社保信息
        List<EmpInfoImportDTO> importSucList = empImportExcelVO.getImportSucList();
        if (isAdd) {
            List<DictItemDO> itemList = adminProviderService.selectAllSocItem();
            log.info("花名册导入数据,社保相关开始,时间戳:{}", System.currentTimeMillis());
            empSocRelatedService.empImportSocDate(importSucList, empInfoDOList, companyId, itemList, tokenUser);
            log.info("花名册导入数据,社保相关结束,时间戳:{}", System.currentTimeMillis());
        }
        CountDownLatch countDownLatch = new CountDownLatch(empInfoDOList.size());

        //  depNameIdMap 部门名称(全路径)-部门Id
        Map<String, String> depNameIdMap = new HashMap<>(16);

        String desc = PersonalEventExample.INSERT_INTO_ADMIN + tokenUser.getUserName() + PersonalEventExample.INSERT_INTO_SYS;
        long l1 = System.currentTimeMillis();
        for (EmpInfoDO empInfo : empInfoDOList) {
            String result = "";
            try {
                if (isAdd) {
                    // 新员工,开通用户
                    userProviderService.registerAccount(empInfo.getName(), empInfo.getPhone(), tokenUser, companyId);

                    //不能直接to,因为education字段类型不同
//                    personalEventService.autoSave(empInfo.to(EmpInfoVO.class), tokenUser, desc, PersonalEventEnum.ADD.getValue(), PersonalEventEnum.ADD.getDesc());
                    EmpInfoVO personalEventEmpInfoVO = new EmpInfoVO();
                    personalEventEmpInfoVO.setCompanyId(empInfo.getCompanyId());
                    personalEventEmpInfoVO.setId(empInfo.getId());
                    personalEventEmpInfoVO.setUserId(empInfo.getUserId());
                    personalEventService.autoSave(personalEventEmpInfoVO, tokenUser, desc, PersonalEventEnum.ADD.getValue(), PersonalEventEnum.ADD.getDesc());
                }
                // 保存部门
                Future<String> saveDept = asyncEmpInfoDetailService.saveDept(empInfo, companyId, depNameIdMap, tokenUser);
                // 保存岗位
                Future<String> savePosition = asyncEmpInfoDetailService.savePosition(empInfo, companyId, tokenUser);
                // 保存合同
                Future<String> saveContract = asyncEmpInfoDetailService.saveContract(empInfo, companyId, tokenUser);

                //保存家庭成员
                Future<String> saveEmpFamily = asyncEmpInfoDetailService.saveEmpFamily(empInfo, companyId, tokenUser);

                //保存紧急联系人
                Future<String> empContacts = asyncEmpInfoDetailService.empContacts(empInfo, companyId, tokenUser);

                // 保存自定义字段信息
                Future<String> saveAddition = asyncEmpInfoDetailService.saveAddition(empInfo, companyId, areaList, tokenUser);
                // 保存学历
                Future<String> saveEducation = asyncEmpInfoDetailService.saveEducation(empInfo, companyId, isAdd, tokenUser);
                // 保存今日新增
                Future<String> saveOperationEmpRecord = asyncEmpInfoDetailService.saveOperationEmpRecord(empInfo,
                        companyId, areaList, isAdd, tokenUser);
                try {
                    String deptResult = saveDept.get();
                    String positionResult = savePosition.get();
                    String contractResult = saveContract.get();
                    String additionResult = saveAddition.get();
                    String educationResult = saveEducation.get();
                    String empFamilyResult = saveEmpFamily.get();
                    String empContactResult = empContacts.get();
                    String saveOperationEmpRecordResult = saveOperationEmpRecord.get();
                    result = deptResult + "\n" + positionResult + "\n" + contractResult + "\n"
                            + empFamilyResult + "\n" + empContactResult + "\n"
                            + additionResult + "\n" + educationResult + "\n" + saveOperationEmpRecordResult;
                } catch (InterruptedException | ExecutionException e) {
                    log.error("花名册导入错误：empId：{}，身份证：{}", empInfo.getId(), empInfo.getIdCard());
                }
            } catch (Exception e) {
                log.error("花名册导入子线程错误：{}", e);
                // 子线程出错更新任务为失败
                adminProviderService.updateTaskFail(asyncTaskId);
                result = e.getMessage();
            } finally {
                // 创建异步记录详情
                AsyncTaskDetailAddDTO asyncTaskDetailAddDTO = new AsyncTaskDetailAddDTO();
                // 子线程没有返回信息，新建员工成功
                result = result.replaceAll("\n", "");
                if (StrKit.isEmpty(result)) {
                    asyncTaskDetailAddDTO.setSubjectResult(TaskSubjectResultEnum.SUCCESS.getValue());
                } else {
                    asyncTaskDetailAddDTO.setSubjectResult(TaskSubjectResultEnum.FAIL.getValue());
                    asyncTaskDetailAddDTO.setErrorMessage(result);

                    // 子线程出错更新任务为失败
                    adminProviderService.updateTaskFail(asyncTaskId);
                }
                asyncTaskDetailAddDTO.setTaskId(asyncTaskId);
                // empId
                asyncTaskDetailAddDTO.setSubjectId(empInfo.getId());
                adminProviderService.saveAsyncDetail(asyncTaskDetailAddDTO, tokenUser);
                //  主任务表更新,已完成数量+1
                adminProviderService.finishSubTask(asyncTaskId);
                // 锁存器-1
                countDownLatch.countDown();
            }
        }
        // 发通知
        sentNotice(isAdd, empInfoDOList, companyId, tokenUser);
        try {
            countDownLatch.await();
            // 如果剩余线程为0，任务结束，删除redis key
            log.info("子线程结束");
            // 主任务改为完成
            AsyncTaskDO asyncTaskUpdateDO = new AsyncTaskDO();
            asyncTaskUpdateDO.setId(asyncTaskId);
            asyncTaskUpdateDO.setTaskEnd(new Date());
            asyncTaskUpdateDO.setTaskStatus(TaskStatusEnum.WAN_CHENG.getValue());
            adminProviderService.updateAsyncTask(asyncTaskUpdateDO, tokenUser);
        } catch (InterruptedException e) {
            log.error("子线程阻塞失败：{}", e.getMessage());
            e.printStackTrace();
        } finally {
            //释放锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }

        long l2 = System.currentTimeMillis();
        log.info("多线程导入完成，耗时{}ms", (l2 - l1));

        //更新员工的 “信息补充 未完善、已完善”
        List<String> list = empInfoDOList.stream().map(EmpInfoDO::getId).collect(Collectors.toList());
        empInfoService.empCompleteInfo(list, tokenUser);

    }

    @Override
    @Async
    public void asyncBatchSaveBySite(EmpImportExcelVO empImportExcelVO, boolean isAdd, TokenUser tokenUser) {
        long l1 = System.currentTimeMillis();
        // 导入成功的员工
        List<EmpInfoImportDTO> importSucList = empImportExcelVO.getImportSucList();
        // 导入成功员工按用工单位分组
        Map<String, List<EmpInfoImportDTO>> companyIdListMap = importSucList.parallelStream()
                .collect(Collectors.groupingBy(EmpInfoImportDTO::getCompanyId));
        //  创建员工信息
        List<EmpInfoDO> empInfoDOList;
        if (isAdd) {
            empInfoDOList = empImportExcelVO.getEmpInfoAddList();
        } else {
            empInfoDOList = empImportExcelVO.getEmpInfoUpdateList();
        }
        String siteId = tokenUser.getSiteId();
        Map<String, SiteDO> siteMap = adminProviderService.selectSiteMap();
        SiteDO siteDO = siteMap.get(siteId);
        if (siteDO == null) {
            log.error("所在租户不存在, 租户Id：{}", siteId);
            throw new ParamException("所在租户不存在");
        }
        AsyncTaskDO asyncTaskDO = new AsyncTaskDO();
        asyncTaskDO.setTaskName("租户：【" + siteDO.getName() + "】-花名册批量导入-" + DateKit8.format(new Date(), "yyyy-MM-dd HH:mm"));
        asyncTaskDO.setTaskStart(new Date());
        asyncTaskDO.setTotalTask(empInfoDOList.size());
        asyncTaskDO.setTaskStatus(TaskStatusEnum.JIN_XING_ZHONG.getValue());
        asyncTaskDO.setTaskType(AsyncTaskTypeEnum.EMP_IMPORT.getValue());
        asyncTaskDO.setTaskMethod("asyncBatchSaveBySite");
        AsyncTaskDO asyncTask = adminProviderService.saveAsyncTask(asyncTaskDO, tokenUser);
        String asyncTaskId = asyncTask.getId();
        log.info("====开始多线程导入,任务id：{}====", asyncTaskId);
        //  1.校验redis 是否存在正在导入操作
        String lockKey = CacheKeyKit.getEmpInfoImportKey(siteId);
        RLock lock = redissonClient.getLock(lockKey);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后60分钟自动解锁
            hasLock = lock.tryLock(0, 60, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            log.error("异步导入员工信息失败：{}，lockKey：{}", e.getMessage(), lockKey);
            // 任务为失败
            adminProviderService.updateTaskFail(asyncTaskId);
            return;
        }
        if (!hasLock) {
            log.warn("花名册导入重复点击");
            // 任务为失败
            adminProviderService.updateTaskFail(asyncTaskId);
            return;
        }
        CountDownLatch countDownLatch = new CountDownLatch(empInfoDOList.size());
        try {
            // 导入员工按用工单位分组
            Map<String, List<EmpInfoDO>> companyListMap = empInfoDOList.parallelStream()
                    .collect(Collectors.groupingBy(EmpInfoDO::getCompanyId));
            for (String companyId : companyIdListMap.keySet()) {
                EmpImportExcelVO companyImportExcelVO = empImportExcelVO.to(EmpImportExcelVO.class);
                companyImportExcelVO.setImportSucList(companyIdListMap.get(companyId));
                if (isAdd) {
                    companyImportExcelVO.setEmpInfoAddList(companyListMap.get(companyId));
                } else {
                    companyImportExcelVO.setEmpInfoUpdateList(companyListMap.get(companyId));
                }
                asyncBatchSave2(companyImportExcelVO, companyId, isAdd, asyncTaskId, countDownLatch, tokenUser);
                // 发通知
                sentNotice(isAdd, empInfoDOList, companyId, tokenUser);

            }
            countDownLatch.await();
            // 如果剩余线程为0，任务结束，删除redis key
            log.info("子线程结束");
            // 主任务改为完成
            AsyncTaskDO asyncTaskUpdateDO = new AsyncTaskDO();
            asyncTaskUpdateDO.setId(asyncTaskId);
            asyncTaskUpdateDO.setTaskEnd(new Date());
            asyncTaskUpdateDO.setTaskStatus(TaskStatusEnum.WAN_CHENG.getValue());
            adminProviderService.updateAsyncTask(asyncTaskUpdateDO, tokenUser);

            long l2 = System.currentTimeMillis();
            log.info("多线程导入完成，耗时{}ms", (l2 - l1));

            //更新员工的 “信息补充 未完善、已完善”
            List<String> list = empInfoDOList.stream().map(EmpInfoDO::getId).collect(Collectors.toList());
            empInfoService.empCompleteInfo(list, tokenUser);
        } catch (InterruptedException e) {
            log.error("子线程阻塞失败：{}", e.getMessage());
            e.printStackTrace();
        } finally {
            //释放锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }

    /**
     * 保存花名册导入信息 与上方asyncBatchSave逻辑一致  只是异步任务 在asyncBatchSaveBySite方法中创建
     *
     * @param empImportExcelVO 导入数据
     * @param companyId        用工单位id
     * @param isAdd            是否为新增操作；true：新增，false：修改
     * @param asyncTaskId      异步任务Id
     * @param countDownLatch
     * @param tokenUser        当前用户
     */
    private void asyncBatchSave2(EmpImportExcelVO empImportExcelVO, String companyId, boolean isAdd
            , String asyncTaskId, CountDownLatch countDownLatch, TokenUser tokenUser) throws InterruptedException {
        //  创建员工信息
        List<EmpInfoDO> empInfoDOList;
        if (isAdd) {
            empInfoDOList = empImportExcelVO.getEmpInfoAddList();
        } else {
            empInfoDOList = empImportExcelVO.getEmpInfoUpdateList();
        }
        // 所有地区
        List<AreaReduceVO> areaList = adminProviderService.listAllArea();
        // 创建empInfo信息
        saveEmpInfoData(empInfoDOList, isAdd, companyId, areaList, tokenUser);
        // 创建社保信息
        List<EmpInfoImportDTO> importSucList = empImportExcelVO.getImportSucList();
        if (isAdd) {
            List<DictItemDO> itemList = adminProviderService.selectAllSocItem();
            log.info("花名册导入数据,社保相关开始,时间戳:{}", System.currentTimeMillis());
            empSocRelatedService.empImportSocDate(importSucList, empInfoDOList, companyId, itemList, tokenUser);
            log.info("花名册导入数据,社保相关结束,时间戳:{}", System.currentTimeMillis());
        }


        //  depNameIdMap 部门名称(全路径)-部门Id
        Map<String, String> depNameIdMap = new HashMap<>(16);

        String desc = PersonalEventExample.INSERT_INTO_ADMIN + tokenUser.getUserName() + PersonalEventExample.INSERT_INTO_SYS;
        for (EmpInfoDO empInfo : empInfoDOList) {
            String result = "";
            try {
                if (isAdd) {
                    // 新员工,开通用户
                    userProviderService.registerAccount(empInfo.getName(), empInfo.getPhone(), tokenUser, companyId);

                    //不能直接to,因为education字段类型不同
//                    personalEventService.autoSave(empInfo.to(EmpInfoVO.class), tokenUser, desc, PersonalEventEnum.ADD.getValue(), PersonalEventEnum.ADD.getDesc());
                    EmpInfoVO personalEventEmpInfoVO = new EmpInfoVO();
                    personalEventEmpInfoVO.setCompanyId(empInfo.getCompanyId());
                    personalEventEmpInfoVO.setId(empInfo.getId());
                    personalEventEmpInfoVO.setUserId(empInfo.getUserId());
                    personalEventService.autoSave(personalEventEmpInfoVO, tokenUser, desc, PersonalEventEnum.ADD.getValue(), PersonalEventEnum.ADD.getDesc());
                }
                // 保存部门
                Future<String> saveDept = asyncEmpInfoDetailService.saveDept(empInfo, companyId, depNameIdMap, tokenUser);
                // 保存岗位
                Future<String> savePosition = asyncEmpInfoDetailService.savePosition(empInfo, companyId, tokenUser);
                // 保存合同
                Future<String> saveContract = asyncEmpInfoDetailService.saveContract(empInfo, companyId, tokenUser);
                //保存家庭成员
                Future<String> saveEmpFamily = asyncEmpInfoDetailService.saveEmpFamily(empInfo, companyId, tokenUser);
                //保存紧急联系人
                Future<String> empContacts = asyncEmpInfoDetailService.empContacts(empInfo, companyId, tokenUser);
                // 保存自定义字段信息
                Future<String> saveAddition = asyncEmpInfoDetailService.saveAddition(empInfo, companyId, areaList, tokenUser);
                // 保存学历
                Future<String> saveEducation = asyncEmpInfoDetailService.saveEducation(empInfo, companyId, isAdd, tokenUser);
                // 保存今日新增
                Future<String> saveOperationEmpRecord = asyncEmpInfoDetailService.saveOperationEmpRecord(empInfo,
                        companyId, areaList, isAdd, tokenUser);
                try {
                    String deptResult = saveDept.get();
                    String positionResult = savePosition.get();
                    String contractResult = saveContract.get();
                    String additionResult = saveAddition.get();
                    String educationResult = saveEducation.get();
                    String empFamilyResult = saveEmpFamily.get();
                    String empContactResult = empContacts.get();
                    String saveOperationEmpRecordResult = saveOperationEmpRecord.get();
                    result = deptResult + "\n" + positionResult + "\n" + contractResult + "\n"
                            + empFamilyResult + "\n" + empContactResult + "\n"
                            + additionResult + "\n" + educationResult + "\n" + saveOperationEmpRecordResult;
                } catch (InterruptedException | ExecutionException e) {
                    log.error("花名册导入错误：empId：{}，身份证：{}", empInfo.getId(), empInfo.getIdCard());
                    e.printStackTrace();
                }
            } catch (Exception e) {
                log.error("花名册导入子线程错误：{}", e);
                // 子线程出错更新任务为失败
                adminProviderService.updateTaskFail(asyncTaskId);
                result = e.getMessage();
            } finally {
                // 创建异步记录详情
                AsyncTaskDetailAddDTO asyncTaskDetailAddDTO = new AsyncTaskDetailAddDTO();
                // 子线程没有返回信息，新建员工成功
                result = result.replaceAll("\n", "");
                if (StrKit.isEmpty(result)) {
                    asyncTaskDetailAddDTO.setSubjectResult(TaskSubjectResultEnum.SUCCESS.getValue());
                } else {
                    asyncTaskDetailAddDTO.setSubjectResult(TaskSubjectResultEnum.FAIL.getValue());
                    asyncTaskDetailAddDTO.setErrorMessage(result);

                    // 子线程出错更新任务为失败
                    adminProviderService.updateTaskFail(asyncTaskId);
                }
                asyncTaskDetailAddDTO.setTaskId(asyncTaskId);
                // empId
                asyncTaskDetailAddDTO.setSubjectId(empInfo.getId());
                adminProviderService.saveAsyncDetail(asyncTaskDetailAddDTO, tokenUser);
                //  主任务表更新,已完成数量+1
                adminProviderService.finishSubTask(asyncTaskId);
                // 锁存器-1
                countDownLatch.countDown();
            }
        }
    }

    /**
     * 发送转正通知和入职通知
     */
    private void sentNotice(boolean isAdd, List<EmpInfoDO> empInfoDOList, String companyId, TokenUser tokenUser) {
        log.info("花名册导入数据,发送通知开始,时间戳:{}", System.currentTimeMillis());
        try {
            if (isAdd && !CollectionUtils.isEmpty(empInfoDOList)) {
                //发送入职通知
                String subjectId = empInfoDOList.stream().map(EmpInfoDO::getId).collect(Collectors.joining(","));
                sendNotice(DateUtil.offsetMinute(new Date(), 1), FORMAL.getValue(), subjectId, companyId, tokenUser);
                //发送待转正通知
                List<EmpInfoDO> dtoList = empInfoDOList.stream().filter(a -> a.getFormalType().equals(PROBATION.getValue()) && a.getTurnStraightDate() != null).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(dtoList)) {
                    //按转正日期分组
                    Map<Date, List<EmpInfoDO>> listMap = dtoList.stream().collect(Collectors.groupingBy(EmpInfoDO::getTurnStraightDate));
                    for (Date date : listMap.keySet()) {
                        List<EmpInfoDO> infoImportDTOList = listMap.get(date);
                        subjectId = infoImportDTOList.stream().map(EmpInfoDO::getId).collect(Collectors.joining(","));
                        sendNotice(date, PROBATION.getValue(), subjectId, companyId, tokenUser);
                    }

                }
            }
        } catch (Exception e) {
            log.error("花名册导入消息通知失败，失败原因：{}", e.getMessage());
        }
        log.info("花名册导入数据,发送通知结束,时间戳:{}", System.currentTimeMillis());
    }

    /**
     * 保存员工信息
     */
    private void saveEmpInfoData(List<EmpInfoDO> empInfoList, boolean isAdd, String companyId, List<AreaReduceVO> areaList, TokenUser tokenUser) {
        // 员工信息补全
        log.info("花名册导入数据,员工信息补全开始,时间戳:{}", System.currentTimeMillis());
        empInfoList.parallelStream().forEach(
                empInfo -> {
                    // 新增
                    if (isAdd) {
                        // 证件类型(默认居民身份证)
                        if (empInfo.getCertificateType() == null) {
                            empInfo.setCertificateType(CertificateTypeEnum.ID_CARD.getValue());
                        }
                        // 国籍 默认中国
                        if (empInfo.getNationality() == null) {
                            empInfo.setNationality(NATIONALITY);
                        }
                        // 任职受雇从业类型(默认雇员)
                        if (empInfo.getEmploymentType() == null) {
                            empInfo.setEmploymentType(EMPLOYMENT_TYPE);
                        }
                        // 薪资档案类型；0：未定薪 1：固定薪资档案 2：工价薪资档案
                        if (empInfo.getSalaryType() == null) {
                            empInfo.setSalaryType(SalaryFileTypeEnum.NO_SALARY.getValue());
                        }
                        if (empInfo.getEmployType() == null) {
                            // 聘用形式（0：非正式，1：正式）
                            empInfo.setEmployType(true);
                        }
                        if (empInfo.getFormalType() == null) {
                            // 正式类型（0：无状态，1：试用期，2：已转正）
                            empInfo.setFormalType(2);
                        }
                        if (empInfo.getJoinFormStatus() == null) {
                            // 入职登记表状态（0：未提交，1：已提交）
                            empInfo.setJoinFormStatus(false);
                        }
                        if (empInfo.getEmpSocStatus() == null) {
                            // 未参保
                            empInfo.setEmpSocStatus(NOT_SOC.getValue());
                        }
                        if (empInfo.getPostType() == null) {
                            // 岗位类型
                            empInfo.setPostType(PostType.LABOUR.getValue());
                        }
                        if (empInfo.getContractsStatus() == null) {
                            // 合同状态（0:合同未发起;1:待员工签署;2:带公司签署;3:签署完成）
                            empInfo.setContractsStatus(0);
                        }
                        //实名认证
                        empInfo.setVerified(false);
                        // 员工变动状态（0：当前无变动，1：转正中，2：调岗中，3： 离职中）
                        empInfo.setChangeStatus(0);
                        // 员工状态
                        empInfo.setStatus(HAS_ENTRY.getValue());
                        // 审批状态
                        empInfo.setApprovalStatus(ApprovalStatusEnum.APPRO_DISABLE.getValue());
                        //添加信息完善默认值默认值
                        empInfo.setIsImprove(false);
                        //给名字添加拼音
                        empInfo.setNamePinyin(PinyinKit.nameToPinyin(empInfo.getName()));
                        // 通用字段
                        empInfo.setCompanyId(companyId);
                        empInfo.preInsert(tokenUser.getUserId());
                        empInfo.setSiteId(tokenUser.getSiteId());
                        if (empInfo.getIdCard() != null) {
                            String idCard = empInfo.getIdCard();
                            if (RegUtils.isIdCard(idCard)) {
                                // 拿身份证号辨别性别,年龄,身份证号
                                if (empInfo.getIdCard().length() == 18) {
                                    empInfo.setGender(Integer.parseInt(idCard.substring(16, 17)) % 2 == 0 ? 2 : 1);
                                    empInfo.setEmpAge(Integer.parseInt(DateUtils.getYearAndMonth(new Date())[0]) - Integer.parseInt(idCard.substring(6, 10)));
                                    empInfo.setBirthday(idCard.substring(6, 10) + "-" + idCard.substring(10, 12) + "-" + idCard.substring(12, 14) + " 00:00:00");
                                } else if (empInfo.getIdCard().length() == 15) {
                                    empInfo.setGender(Integer.parseInt(idCard.substring(13, 14)) % 2 == 0 ? 2 : 1);
                                    empInfo.setEmpAge(Integer.parseInt(DateUtils.getYearAndMonth(new Date())[0]) - Integer.parseInt(idCard.substring(6, 8)) - 1901);
                                    empInfo.setBirthday("19" + idCard.substring(6, 8) + "-" + idCard.substring(8, 10) + "-" + idCard.substring(10, 12) + " 00:00:00");
                                }
                            }
                        }

                    }
                    //入职城市
                    if (StringUtils.isNotBlank(empInfo.getJoinCityName())) {
                        empInfo.setJoinCityCode(getAreaIdByName(areaList, empInfo.getJoinCityName()));
                    }

                    //工资卡开户城市
                    if (StringUtils.isNotBlank(empInfo.getBankPlace())) {
                        empInfo.setBankPlace(getAreaIdByName(areaList, empInfo.getBankPlace()));
                    }
                    // 渠道
                    if (StringUtils.isNotBlank(empInfo.getRecruitChannels())) {
                        ChannelVO channelVO = channelService
                                .selectByName(empInfo.getRecruitChannels(), tokenUser.getSiteId());
                        if (channelVO != null) {
                            empInfo.setChannelId(channelVO.getId());
                        }
                    }
                    // 根据入职时间和转正时间获取员工状态
                    checkEmpProbationAndFormalBatch(empInfo);
                }
        );
        log.info("花名册导入数据,员工信息补全结束,时间戳:{}", System.currentTimeMillis());
        // 员工信息更新或保存
        if (isAdd) {
            // 新增先批量保存一下,生成主键id,便于后续使用
            empInfoService.insertListAndSetId(empInfoList);
        } else {
            //修改退休信息发送退休通知
            sendRetirement(empInfoList);
            empInfoList.forEach(empInfoDO -> {
                EmpInfoDO empInfoDOItem = empInfoService.selectById(empInfoDO.getId());
                if (!StringUtils.isEmpty(empInfoDO.getBankCard()) && !StringUtils.isEmpty(empInfoDOItem.getBankCard()) && !empInfoDO.getBankCard().equals(empInfoDOItem.getBankCard())) {
                    //工资条更改变动通知
                    try {
                        empInfoDO.setUserId(empInfoDOItem.getUserId());
                        empInfoDO.setCompanyId(companyId);
                        sendNoticeService.salaryChangeSendNotice(empInfoDO, companyId, tokenUser);
                    } catch (Exception e) {
                        log.error("员工工资卡变更发送消息失败,{}", e.getMessage());
                    }
                }
                empInfoService.updateByIdSelective(empInfoDO, empInfoDO.getSiteId(), tokenUser.getUserId());
                //修改预登记渠道
                updateCheckInChannel(empInfoDO, tokenUser);
            });
        }
    }

    /**
     * 获取地区id
     */
    private String getAreaIdByName(List<AreaReduceVO> areaList, String name) {
        if (StringUtils.isEmpty(name)) {
            return null;
        }
        // 员工所属地区
        AreaReduceVO areaReduce = adminProviderService.getAreaByName(areaList, name);
        if (Objects.nonNull(areaReduce)) {
            // 放入员工中
            return areaReduce.getId() == null ? null : areaReduce.getId().toString();
        }
        return null;
    }

    /**
     * 批量保存时正式类型/入职时间/试用天数/转正天数的处理
     *
     * @param empInfoDO 员工信息
     */
    private void checkEmpProbationAndFormalBatch(EmpInfoDO empInfoDO) {
        // 如果填了，已填的为准
        if (Objects.nonNull(empInfoDO.getFormalType())) {
            return;
        }
        if (empInfoDO.getJoinDate() == null && empInfoDO.getTurnStraightDate() == null) {
            return;
        }

        // 入职时间
        Date joinDate = empInfoDO.getJoinDate();
        // 转正时间
        Date turnStraightDate = empInfoDO.getTurnStraightDate();
        // 试用天数
        Integer probationDays = empInfoDO.getProbationDays();

        if (turnStraightDate == null && joinDate != null && probationDays != null) {
            // 没有转正时间,根据入职时间和试用天数计算下转正时间
            turnStraightDate = org.apache.commons.lang3.time.DateUtils.addDays(empInfoDO.getJoinDate(), empInfoDO.getProbationDays());
        }
        empInfoDO.setFormalType(FormalTypeEnum.FORMAL.getValue());
        if (joinDate != null) {
            // 有入职时间,算试用期
            empInfoDO.setFormalType(PROBATION.getValue());
        }
        if (turnStraightDate != null) {
            // 获取明天时间0点
            Date dateTomorrow = DateUtils.getTomorrow();
            // 转正时间明天之前,已转正
            if (turnStraightDate.before(dateTomorrow)) {
                empInfoDO.setFormalType(FormalTypeEnum.FORMAL.getValue());
            } else {
                empInfoDO.setFormalType(PROBATION.getValue());
            }
            empInfoDO.setTurnStraightDate(turnStraightDate);
        }
    }

    /**
     * 修改退休信息发送退休通知
     * <p>
     * 有退休日期，原来有没有，原来没有，发送，原来有，对比时间是否一样，一样不发不一样发
     * 没有退休日期
     * 有退休年龄，没有出生日期，看原来有没有出生日期，没有就不发，有的话看原来年龄有没有，没有就发，有的话再看是否一样，一样不发，不一样发
     * 有退休年龄，有出生日期，看原来有没有出生日期和退休年龄
     * 原来没有出生日期和退休年龄，发送
     * 原来没有出生日期有退休年龄，退休年龄是否一样，不一样不发送，一样的话发送
     * 原来有出生日期和退休年龄，发送,看退休年龄是否一样，不一样不发送，一样的话再看出生年月，出生日期一样不发送，不一样发送
     * 没有退休年龄，没有出生日期，不发送
     *
     * @param empInfoDOList 员工信息list
     */
    private void sendRetirement(List<EmpInfoDO> empInfoDOList) {
        try {
            NoticeRuleSettingVO ruleSettingVO = noticeProviderService.selectByTypeCodeAndCompany(HrConstants.HR_SUBJECT_TYPE,
                    NoticeConstants.RETIRE, empInfoDOList.get(0).getCompanyId());
            if (ruleSettingVO == null || ruleSettingVO.getId() == null || !ruleSettingVO.getEnableStatus()) {
                return;
            }
            List<EmpInfoDO> empInfoDOS = new ArrayList<>();
            for (EmpInfoDO empInfoDO : empInfoDOList) {
                EmpInfoDO empInfoDO1 = empInfoService.selectById(empInfoDO.getId());
                if (empInfoDO.getRetirementDate() != null) {
                    if (empInfoDO1.getRetirementDate() == null) {
                        empInfoDO1.setRetirementDate(empInfoDO.getRetirementDate());
                        empInfoDOS.add(empInfoDO1);
                    } else if (empInfoDO1.getRetirementDate() != null && !DateUtil.isSameTime(empInfoDO1.getRetirementDate(), empInfoDO.getRetirementDate())) {
                        empInfoDO1.setRetirementDate(empInfoDO.getRetirementDate());
                        empInfoDOS.add(empInfoDO1);
                    }
                } else {
                    if (empInfoDO.getRetirementAge() != null && empInfoDO.getBirthday() == null) {
                        if (empInfoDO1.getBirthday() != null) {
                            if (empInfoDO1.getRetirementAge() == null) {
                                empInfoDO1.setRetirementDate(DateUtil.offset(DateUtil.parse(empInfoDO1.getBirthday()), DateField.YEAR, empInfoDO.getRetirementAge()));
                                empInfoDOS.add(empInfoDO1);
                            } else if (empInfoDO1.getRetirementAge().equals(empInfoDO.getRetirementAge())) {
                                empInfoDO1.setRetirementDate(DateUtil.offset(DateUtil.parse(empInfoDO1.getBirthday()), DateField.YEAR, empInfoDO.getRetirementAge()));
                                empInfoDOS.add(empInfoDO1);
                            }
                        }
                    } else if (empInfoDO.getRetirementAge() != null && empInfoDO.getBirthday() != null) {
                        if (empInfoDO1.getRetirementAge() == null && empInfoDO1.getBirthday() == null) {
                            empInfoDO1.setRetirementDate(DateUtil.offset(DateUtil.parse(empInfoDO1.getBirthday()), DateField.YEAR, empInfoDO.getRetirementAge()));
                            empInfoDOS.add(empInfoDO1);
                        } else if (empInfoDO1.getRetirementAge() != null && empInfoDO1.getBirthday() == null) {
                            if (empInfoDO1.getRetirementAge().equals(empInfoDO.getRetirementAge())) {
                                empInfoDO1.setRetirementDate(DateUtil.offset(DateUtil.parse(empInfoDO.getBirthday()), DateField.YEAR, empInfoDO.getRetirementAge()));
                                empInfoDOS.add(empInfoDO1);
                            }
                        } else if (empInfoDO1.getRetirementAge() != null && empInfoDO1.getBirthday() != null) {
                            if (empInfoDO1.getRetirementAge().equals(empInfoDO.getRetirementAge())) {
                                if (!empInfoDO1.getBirthday().equals(empInfoDO.getBirthday())) {
                                    empInfoDO1.setRetirementDate(DateUtil.offset(DateUtil.parse(empInfoDO.getBirthday()), DateField.YEAR, empInfoDO.getRetirementAge()));
                                    empInfoDOS.add(empInfoDO1);
                                }
                            }
                        }
                    }
                }
            }
            if (CollectionUtils.isEmpty(empInfoDOS)) {
                return;
            }
            sendNoticeTask.addTask(empInfoDOS, NoticeConstants.RETIRE, "员工退休发送通知", ReceiveDateTypeEnum.FIXED.getDesc(), new Date());
        } catch (Exception e) {
            log.info("人员修改退休日期发送通知失败");
        }
    }

    /**
     * 修改预登记渠道
     *
     * @param empInfoDO 员工信息
     * @param tokenUser 当前用户
     */
    private void updateCheckInChannel(EmpInfoDO empInfoDO, TokenUser tokenUser) {
        try {
            if (StringUtils.isEmpty(empInfoDO.getChannelId())) {
                return;
            }
            CheckInPeopleDO checkInPeopleDO = checkInPeopleService.selectByPhone(empInfoDO.getPhone(), tokenUser.getSiteId());
            if (checkInPeopleDO == null) {
                return;
            }
            CheckInPeopleUpdateDTO checkInPeopleUpdateDTO = checkInPeopleDO.to(CheckInPeopleUpdateDTO.class);
            checkInPeopleUpdateDTO.setChannelId(empInfoDO.getChannelId());
            checkInPeopleService.update(checkInPeopleUpdateDTO, tokenUser);
        } catch (Exception e) {
            log.error("花名册导入预登记渠道修改错误，错误数据：{}", JSONObject.toJSONString(empInfoDO));
        }
    }

    /**
     * 添加待入职/入职员工发通知
     *
     * @param runDate   运行时间
     * @param type      发送通知类型 2为入职通知 1为入职和待转正通知
     * @param subjectId 业务id
     * @param companyId 用工单位id
     * @param tokenUser 当前用户
     */
    private void sendNotice(Date runDate, Integer type, String subjectId, String companyId, TokenUser tokenUser) {
        try {
            // 添加入职员工
            if (type.equals(FORMAL.getValue())) {
                sendNoticeTask.addTask(subjectId, NoticeConstants.INDUCTION, companyId,
                        runDate, "人员入职发送通知", tokenUser);
            } else if (type.equals(PROBATION.getValue())) {
                sendNoticeTask.addTask(subjectId, NoticeConstants.PRE_TURN_OFFICIAL, companyId,
                        runDate, "人员待转正发送通知", tokenUser);
            }
        } catch (Exception e) {
            log.error("添加入职员工发通知失败，错误信息：{}", e.getMessage());
        }
    }


}
